<?php
// +----------------------------------------------------------------------
// | Description: 创建符合restful风格的控制器
// +----------------------------------------------------------------------
// | Author: dongpeng
// +----------------------------------------------------------------------
namespace app\command;

use app\mysql\Schema;
use think\console\Command;
use think\console\Input;
use think\console\input\Argument;
use think\console\input\Option;
use think\console\Output;

class RestConsole extends Command
{
    private $domain = "framework.com"; // 域名

    protected function configure()
    {
        //设置参数(大小写敏感)
        $this->addArgument('moduleName', Argument::REQUIRED); //模块名称
        $this->addArgument('className', Argument::REQUIRED); //类名
        $this->addArgument('modelName', Argument::REQUIRED); //model类名
        $this->addArgument('tableName', Argument::REQUIRED); //表名
        // 选项
        $this->addOption('Description', 'd', Option::VALUE_REQUIRED, 'test'); //11111
        $this->addOption('Author', 'a', Option::VALUE_OPTIONAL, 'test'); //作者

        $this->setName('rest')->setDescription('create restFul API');
    }

    /**
     * 可以这样执行命令 php think rest front Blobs Blod front_blob -d"this is description"
     * @param Input $input
     * @param Output $output
     * <a href='/@return'>@小鱼儿</a> void;
     */
    protected function execute(Input $input, Output $output)
    {
        // 获取参数值
        $args = $input->getArguments();
        
        // 获取选项值
        $options = $input->getOptions();
        $options['Description'] = $this -> detect_gb18030($options['Description']);
        $Description = $options['Description'];

        $curdModel = file_get_contents(__DIR__ . '/../tpl/CurdModel.tpl');
        $RestController = file_get_contents(__DIR__ . '/../tpl/RestController.tpl');
        $CurdValidate = file_get_contents(__DIR__ . '/../tpl/CurdValidate.tpl');

        $all = $args + $options;
        $all['tableNameHump'] = preg_replace_callback('/([-_]+([a-z]{1}))/i', function ($matches) {
            return strtoupper($matches[2]);
        }, $all['tableName']);

        $schema = new Schema();

        // 判断表是否已经建立  已经创建这里直接跳过
        $tableName = $args['tableName'];
        $tableExist = $schema->tableExist("SELECT table_name FROM information_schema.TABLES WHERE table_name ='$tableName'");
        if (!count($tableExist)) {
            // 如果存在同名sql 则读取创建表的 sql 并运行  否则提示用户创建相关sql文件
            if (is_file(__DIR__ . '/../schema/' . $args['tableName'] . '.sql')) {
                // 运行这段sql语句 创建表
                $schema->createTable(__DIR__ . '/../schema/' . $args['tableName'] . '.sql');
            } else {
                $output->writeln('Lack of SQL files; name: ' . $args['tableName'] . '.sql' . " position: /applacation/schema/");die;
            }
        }

        $tableStruct = $schema->query("SHOW FULL FIELDS FROM " . $args['tableName']);

        // p($tableStruct);
        $rule = '';
        $message = '';
        $params = [];
        $ListTable = '';
        $AddTable = '';
        $EditTable = '';
        foreach ($tableStruct as $k => $v) {
            $field = $v['Field'];
            $comment = $v['Comment'];
            $type = $v['Type'];

            if ($ListTable) {
                 $ListTable .= "
    | {$field} | {$type} | {$comment} |";
            } else {
                $ListTable = "| {$field} | {$type} | {$comment} |";
            }

            if ($v['Key'] != "PRI" and $v['Default'] === null) {

                $tHeader[]      = $comment;
                $filterVal[]    = $field;

                // 页面提交必填项
                if(strpos($comment, '(单选)') !== false){   // 如果注释中使用 (单选)  标明  则该项为单选项

                    $AddSelect[]   =  [ 'prop'=> $field, 'label'=> $comment ];

                    $AddSelectData[$field] = null;
                }else{
                    $AddInput[]   =  [ 'prop'=> $field, 'label'=> $comment ];
                    $AddForm[$field]   =  null; 
                }

                // char 类型
                if( strpos($type, 'char') !== false){
                    $AddRules[$field]  =  [[ 'required'=> true, 'message'=> "请输入".$comment ]];
                    $params[$field] = 'this is '.$field;
                }
                // int 类型
                if( strpos($type, 'int') !== false){
                    $params[$field] = 1;
                }

                if ($AddTable) {
                    $AddTable .= "
       | {$field} | {$type} | 是 |{$comment} |";
               } else {
                   $AddTable = "| {$field} | {$type} | 是 | {$comment} |";
               }

               if ($EditTable) {
                $EditTable .= "
   | {$field} | {$type} | 否 |{$comment} |";
           } else {
               $EditTable = "| {$field} | {$type} | 否 | {$comment} |";
           }

                if ($rule) {
                    $rule .= "
        '$field'        => 'require',";
                    $message .= "
        '{$field}.require'    => '请输入{$comment}',";
                } else {
                    $rule = "'$field'        => 'require',";
                    $message = "'{$field}.require'    => '请输入{$comment}',";
                }
            }
        }

        // 待替换变量
        $all['tableNameHump'] = ucfirst($all['tableNameHump']);
        $all['date'] = date("Y-m-d H:i:s");
        $all['rule'] = $rule;
        $all['message'] = $message;
        if (empty($all['Author'])) {
            $all['Author'] = "董鹏";
        }

        // 替换变量
        foreach ($all as $k => $v) {
            $waitReplace[] = '{{$' . $k . '}}';
            $replace[] = $v;
        }
        $RestController = str_replace($waitReplace, $replace, $RestController);
        $curdModel = str_replace($waitReplace, $replace, $curdModel);
        $CurdValidate = str_replace($waitReplace, $replace, $CurdValidate);

        // 判断模块是否存在 不存在则创建
        $dir = iconv("UTF-8", "GBK", __DIR__ . '/../' . $args['moduleName']);
        // p($dir);
        if (!file_exists($dir)) {
            mkdir(__DIR__ . '/../' . $args['moduleName'], 0777, true);
            mkdir(__DIR__ . '/../' . $args['moduleName'] . '/controller/', 0777, true);
            mkdir(__DIR__ . '/../' . $args['moduleName'] . '/model/', 0777, true);
            mkdir(__DIR__ . '/../' . $args['moduleName'] . '/validate/', 0777, true);
        }

        file_put_contents(__DIR__ . '/../' . $args['moduleName'] . '/controller/' . $args['className'] . '.php', $RestController);
        file_put_contents(__DIR__ . '/../' . $args['moduleName'] . '/model/' . $args['modelName'] . '.php', $curdModel);
        file_put_contents(__DIR__ . '/../' . $args['moduleName'] . '/validate/' . $all['tableNameHump'] . '.php', $CurdValidate);

        // 新增rest路由
        $route = file_get_contents(__DIR__ . '/../../config/route_admin.php');

        //通过正则 获取 rest 设置区域
        $regex = "/'__rest__'=>\[.+\],/Uis";
        preg_match($regex, $route, $matches);
        $strMatches = $matches[0];
        $moduleName = $args['moduleName'];
        $className = $args['className'];

        // 检查该路由是否已经存在
        $count = substr_count($strMatches, "{$moduleName}/{$className}");

        // 已经存在的话  就不在继续增加了
        if ($count != 2) {
            $strMatches = substr_replace($strMatches, "    '{$moduleName}/{$className}'								=>'{$moduleName}/{$className}',
        ],", -2);
            $res = preg_replace($regex, $strMatches, $route);

            file_put_contents(__DIR__ . '/../../config/route_admin.php', $res);
        }

        // $Description = iconv("GB2312", "UTF-8", $Description);

        $assigns = [
            'Description' => '描述信息',
            'moduleName' => $moduleName,
            'className' => $className,
        ];
       
        $assigns['ListTable'] = $ListTable;
        $assigns['AddTable'] = $AddTable;
        $assigns['EditTable'] = $EditTable;
        $assigns['IndexTable'] = $ListTable;
        // 对一个接口进行 基于restful风格的访问
        $assigns = $this -> restQuery($moduleName.'/'.$className, $assigns, $params);

        $this->modifyApiMd(__DIR__ . '/../../api.md', $assigns);

        // 自动生成权限和菜单数据
        
        // 生成页面配置文件
        // 新增
        // $AddForm = [
        //     "id" =>  null,
        //     "Author" =>  null,
        //     "Title" =>  null,
        //     "type" =>  null,
        //     "publishDate" =>  null,
        //     "upvote" =>  null
        // ];

        // $AddSelectData = [
        //     "id" =>  null,
        //     "Author" =>  null,
        //     "Title" =>  null,
        //     "type" =>  null,
        //     "publishDate" =>  null,
        //     "upvote" =>  null
        // ];

        $AddTitle = '新增'.$Description;

        // $AddInput   =   [
        //     [ 'prop'=> "id", 'label'=> "id" ],
        //     [ 'prop'=> "Author", 'label'=> "Author" ],
        //     [ 'prop'=> "Title", 'label'=> "Title" ],
        //     [ 'prop'=> "type", 'label'=> "type" ],
        //     [ 'prop'=> "publishDate", 'label'=> "publishDate" ],
        //     [ 'prop'=> "upvote", 'label'=> "upvote" ]
        // ];

        // $AddSelect   =   [
        //     [ 'prop'=> "id", 'label'=> "id" ],
        //     [ 'prop'=> "Author", 'label'=> "Author" ],
        //     [ 'prop'=> "Title", 'label'=> "Title" ],
        //     [ 'prop'=> "type", 'label'=> "type" ],
        //     [ 'prop'=> "publishDate", 'label'=> "publishDate" ],
        //     [ 'prop'=> "upvote", 'label'=> "upvote" ]
        // ];

        // $AddRules   = [
        //     'Author' => [[ 'required'=> true, 'message'=> "请输入作者名称" ]]
        // ];

        // 编辑
        $EditForm = $AddForm;

        $EditSelectData = $AddSelectData;

        $EditTitle = '编辑'.$Description;

        $EditInput   =   $AddInput;

        $EditSelect   =   $AddSelect;

        $EditRules = [];
        // $EditRules   = [
        //     'goodsName' => [[ 'required'=> true, 'message'=> "请输入商品名称" ]]
        // ];

        // 列表配置
        $baseApi = 'blog/Articles';
        $configFileName = $Description.'列表';
        
        // $tHeader = ["id", "Author", "Title", "type", "publishDate", "upvote"];
        // $filterVal = ["id", "Author", "Title", "type", "publishDate", "upvote"];
        $width  =  [200, 200, 200, 200];

        $search_input   =   [
            [
                'name'=> "field",
                'placeholder'=> "fieldName"
            ]
        ];
        $search_select   =   [
            [
                'name'          => "field",
                'placeholder'   => "fieldName",
                'multiple'      => false
            ]
        ];
        $searchReceived = [
            'status'    => [
                [
                    'label'=> "启用",
                    'value'=> "1"
                ],
                [
                    'label'=> "禁用",
                    'value'=> "0"
                ]
            ]
        ];

        $searchExchanged    =   [
            'limit'=> 10,
            'page'=> 1,
            'orderByString'=> ""
        ];

        // 配置数组
        $configArr = [
            'Add'       => [
                'dialogFormVisible' => false,
                'data'              => [
                    'primary'     => '',
                    'form'        => $AddForm,              // 新增字段
                    'selectData'  => $AddSelectData         // 有备选项的字段
                    ],
                'config'            => [
                    'title'     =>   $AddTitle,             // 新增标题
                    'form'      =>   [
                        'input'     =>  $AddInput,          // 新增文本项
                        'select'    =>  $AddSelect          // 新增单选项
                    ],
                    'rules'     =>   $AddRules              // 新增限制规则
                ]
            ],
            'Edit'       => [
                'dialogFormVisible' => false,
                'data'              => [
                    'primary'     => '',
                    'form'        => $EditForm,              // 编辑字段
                    'selectData'  => $EditSelectData         // 有备选项的字段
                    ],
                'config'            => [
                    'title'     =>   $EditTitle,             // 编辑标题
                    'form'      =>   [
                        'input'     =>  $EditInput,          // 编辑文本项
                        'select'    =>  $EditSelect          // 编辑单选项
                    ],
                    'rules'     =>   $EditRules              // 编辑限制规则
                ]
            ],
            'config'    => [
                'baseApi'   => $baseApi,
                'excel'     => [
                    'fileName'      =>      $configFileName,  // 导出的excel表格名称
                    'tHeader'       =>      $tHeader,         // 表头
                    'filterVal'     =>      $filterVal        // 字段名
                ],
                'table'     =>      [
                    'noGroup'   =>      true,
                    'checkbox'  =>      false,
                    'status'    =>      false, 
                    'sort'      =>      [
                        'field'     =>      "custom"
                    ],
                    'show'      =>      [
                        'items'     =>      $tHeader,
                        'prop'      =>      $filterVal,
                        'width'     =>      $width
                    ],
                    'operate'   =>      [
                        'del'       => true,
                        'edit'      => true
                    ]
                ],
                'search'    =>      [
                    'search_input'      =>      $search_input,
                    'search_select'     =>      [

                    ]
                ],
                'page'      =>      [
                    'limits'=> [5, 10, 20, 50, 100],
                    'layout'=> "total, sizes, prev, pager, next, jumper"
                ]
            ],
            'data'      => [
                'received'      =>      [
                    'table' =>[],
                    'count' => null,
                    'search'    =>      $searchReceived
                ],
                'exchanged'     =>      [
                    'search'            =>      $searchExchanged,
                    'multipleSelection' =>  [],
                    'isDelete'          => 0
                ]
            ]
        ];

        // json格式的配置文件
        $configJson = json_encode($configArr, JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT);

        file_put_contents(__DIR__.'/../jsonConfig/test.json', $configJson);

        // 返回成功信息
        $output->writeln('Generated Successfully!');
    }

    /* 同步的修改api文档
     *
     * @return void
     */
    public function modifyApiMd($filePath, $assigns)
    {
        // 检验api文档是否存在
        $this->checkFile($filePath);

        // 读取文档模板
        $api = file_get_contents(__DIR__ . '/../tpl/RestApi.tpl');

        // 处理模板变量
        // 替换变量
        foreach ($assigns as $k => $v) {
            $waitReplace[] = '{{$' . $k . '}}';
            $replace[] = $v;
        }
        
        $api = str_replace($waitReplace, $replace, $api);
        // 插入最新接口文档
        file_put_contents($filePath, $api, FILE_APPEND);
    }

    /**
     * 检查api文档是否存在 不存在则套用模板创建api文档
     *
     * @param [type] $filePath
     * @return void
     */
    public function checkFile($filePath)
    {
        if (!is_file($filePath)) {
            $baseApi = file_get_contents(__DIR__ . '/../tpl/BaseApi.tpl');
            file_put_contents($filePath, $baseApi);
        }
    }

    public function restQuery($path, $assigns, $params)
    {
        $path = 'http://'.$this -> domain.'/'.$path;
        $curl = new \Curl\Curl();

        // Post Add
        $curl->post($path, $params);
        if ($curl->error) {
            $output->writeln('Post data to api failed. Please check the web server!');die;
        } else {
            // 美化json
            $res = json_decode($curl->response, true);   
            $resJson = json_encode($res, JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT);
            $assigns['AddRes'] = $resJson;
        }
    
        // Get List
        $curl->get($path);
        if ($curl->error) {
            $output->writeln('Get dataList from api failed. Please check the web server!');die;
        } else {
            // 美化json
            $res = json_decode($curl->response, true);
            $id = $res['data']['list'][0]['id'];
            $assigns['ListRes'] = json_encode($res, JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT);
        }

        // PUT Ddit
        foreach($params as $k => $v){
            if(is_string($params[$k])){
                $params[$k] = $params[$k]." modified";
            }
            if(is_int($params[$k])){
                $params[$k] = $params[$k] + 1;
            }
        }
        $curl->put($path."/".$id, $params);
        if ($curl->error) {
            $output->writeln('Put data to api failed. Please check the web server!');die;
        } else {
            // 美化json
            $res = json_decode($curl->response);   
            $resJson = json_encode($res, JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT);
            // p($resJson);
            $assigns['EditRes'] = $resJson;
        }

        // Get Index
        $curl->get($path."/".$id);
        if ($curl->error) {
            $output->writeln('Get one items from api failed. Please check the web server!');die;
        } else {
            // 美化json
            $res = json_decode($curl->response);   
            $assigns['IndexRes'] = json_encode($res, JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT);
        }

        // Delete delete
        $curl->delete($path."/".$id);
        if ($curl->error) {
            $output->writeln('Delete data from api failed. Please check the web server!');die;
        } else {
            // 美化json
            $res = json_decode($curl->response);   
            $assigns['DeleteRes'] = json_encode($res, JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT);
        }

        return $assigns;
    }

    /**
     * 兼容ascii，gbk gb2312，识别字符串是否是gb18030标准的中文编码
     * @param string $line
     * @return boolean
     * @author fangl
     */
    function detect_gb18030($line) {
        $gbbyte = 0; //识别出gb字节数
        for($i=0;$i+3<strlen($line);) {
            if(ord($line{$i}) >= 0 && ord($line{$i}) <= 0x7f) {
                $gbbyte ++; //识别一个单字节 ascii
                $i++;
            }
            else if( ord($line{$i}) >= 0x81 && ord($line{$i}) <= 0xfe &&
            (ord($line{$i+1}) >= 0x40 && ord($line{$i+1}) <= 0x7e ||
             ord($line{$i+1}) >= 0x80 && ord($line{$i+1}) <= 0xfe) ) {
                $gbbyte += 2; //识别一个双字节gb18030（gbk）
                $i += 2;
            }
            else if( ord($line{$i}) >= 0x81 && ord($line{$i}) <= 0xfe &&
            ord($line{$i+2}) >= 0x81 && ord($line{$i+2}) <= 0xfe &&
            ord($line{$i+1}) >= 0x30 && ord($line{$i+1}) <= 0x39 &&
            ord($line{$i+3}) >= 0x30 && ord($line{$i+3}) <= 0x39) {
                $gbbyte += 4; //识别一个4字节gb18030（扩展）
                $i += 4;
            }
            else $i++; //未识别gb18030字节
        }
        return abs($gbbyte - strlen($line)) <= 4; //误差在4字节之内
    }
}